(*
     camlflickr
     Copyright (C) 2008  Zachary R. Anderson (zanderso@gmail.com)
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
 
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License along
     with this program; if not, write to the Free Software Foundation, Inc.,
     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)

(**

An OCaml interface to the flickr API.
@author Zach Anderson
@version 0.1
*)

(**
Some convenience functions for accessing Xml-Light values. If an Xml element
called "rsp" has one child called "photos," and it has an arbitrary number
of children called "photo" each of which has an attribute named "owner"
and some element text, then you can say things like:
{ul
{- [getXml "rsp[0]" xml] To get the "photos" element.}
{- [getXmlList "rsp[0].photos[0]" xml] To get the list of photos.}
{- [getXmlVal "rsp[0].photos[3].photo[0].text" xml] To get the element text for
   the fourth photo.}
{- [getXmlVal "rsp[0].photos[0].photo[owner]" xml] To get the attribute called
   "owner" on the first photo, and so forth}
}
All of these methods can raise [XmlExtractionFailure(xmlstr)] if the Xml
doesn't match the string you give, and [XmlMalformedSelector(s)] if the
string you give as first argument is funky.
*)
module Xmlutil : sig

    (** Raised when selector string asks for things that aren't in the xml.
        The string argument is a pretty printed xml element *)
    exception XmlExtractionFailure of string

    (** Raised when the selector string is no good. The string argument is
        the offending selector string *)
    exception XmlMalformedSelector of string

    (** [getXml s xml] gives the xml element specified by the selector string
        [s] in [xml]. *)
    val getXml : string -> Xml.xml -> Xml.xml

    (** [getXmlList s xml] gives a list of xml elements specified by the
        selector string [s] in [xml]. Notice in the example above that
        the thing in square brackets after the last element name is ignored. *)
    val getXmlList : string -> Xml.xml -> Xml.xml list

    (** [getXmlVal s xml] gives the string value specified by the selector
        string [s] in [xml]. This can be an attribute or element text. *)
    val getXmlVal : string -> Xml.xml -> string
end

(**
These are some OCaml types for things that are returned by the API.
It may be more appropriate for thigns like this to go in application
code. We'll see.
*)

(** A type for information about Flickr users *)
type user =
    {
        mutable nsid : string;
        mutable username : string;
        mutable fullname : string;
    }

(** Takes some xml of the form <user nsid="whatever" ...> and converts it to
    a [user].*)
val xmlToUser : Xml.xml -> user

(** A type for information about Flickr photos *)
type photo =
    {
        id : string;
        owner : string;
        secret : string;
        server : string;
        farm : string;
        title : string;
        ispublic : string;
        isfriend : string;
        isfamily : string;
    }

(** Takes some xml of the form <photo id="whatever" ...> and converts it to
    a [photo] *)
val xmlToPhoto : Xml.xml -> photo

(** [downloadPhoto targetDir ~url:url ~sz:sz ph]
   Downloads photo [ph] into directory [targetDir].
   If [url] is "", which is the default, construct the download url from
   [ph] and [sz]. If [url] is specified, [sz] is ignored, and [ph] is used only
   to construct the name of the target file. *)
val downloadPhoto : string -> ?url:string -> ?sz:string -> photo -> unit


(**
The [flickrClass] provides methods for interacting with the Flickr API.
The first argument to the constructor is the API key, and the second is
the API secret for the particular application.
*)
class flickrClass : string -> string -> object
    (*val mutable private token : string*)

    (** [restCall method args] Permforms an authenticated call to flickr method
        [meth] with arguments [args]. Returns a result suitable for parsing
        with Xml_light functions. *)
    method restCall : string -> (string * string) list -> string

    (** [restUnauthCall method args] Performs an unauthenticated call *)
    method restUnauthCall : string -> (string * string) list -> string

    (** [authCall method args] Used for some flickr.auth calls *)
    method authCall : string -> (string * string) list -> string

    (** [uploadCall file args] Uploads [file] to the account of the
        authenticated user. *)
    method uploadCall : string -> (string * string) list -> string

    (** [replaceCall file args] Replaces a photo with [file]. The photo
        id of photo to be replaced should be in args.*)
    method replaceCall : string -> (string * string) list -> string

    (** [getTokenPartOne webbrowser perms] This method first checks to see if
        the flickr user has already granted sufficient permissions for this
        application. If so, the token is checked and returned. If not,
        a frob is requested, and [webbrowser] is passed a url at which the user
        can validate the application. Returns [(frob,token)]*)
    method getTokenPartOne : (string -> unit) -> string -> string * string

    (** [getTokenPartTwo tok frob] If we already have a token, then this does
        nothing. Otherwise, the validated [frob] is used to request a token. *)
    method getTokenPartTwo : string -> string -> string


    (* The above methods are used as follows:
        [let webbrowser url = ignore(Sys.command("firefox \""^url^"\""))
         let (frob,token) = flickr#getTokenPartOne webbrowser "read" in
         if token = "" then ignore(read_line ());
         let token = flickr#getTokenPartTwo token frob in]*)
end

module Flickr_stream : sig

exception FlickrStreamEnd
exception FlickrStreamFailure of string

class type flickrStreamClassType = object
    method getFlickr : flickrClass
    method getNext : unit -> photo
    method getNextN : int -> photo list
    method setLookahead : int -> unit
    method getLookahead : int
end

class virtual flickrStreamClass : flickrClass -> object
    val mutable virtual lookahead : int
    method private virtual populatePhotoList : unit -> unit
    method getFlickr : flickrClass
    method getNext : unit -> photo
    method getNextN : int -> photo list
    method setLookahead : int -> unit
    method getLookahead : int
end

class interestingStreamClass : ?la:int -> flickrClass -> flickrStreamClassType

end
